package com.atwy.graph.sp;

/**
 * @Author: 小王子火
 * @Date: 2022/3/23
 * 单源最短路：Dijkstra算法
 */

/**
 *
 单源最短路径
 单源最短路径问题是在一个带权重的有向图中寻找从一个特定顶点到其他顶点的最短路径，其中最短路径通过边的权重之和来度量。‌Dijkstra算法是一种常用的解决单源最短路径问题的贪心算法，它从源顶点开始，逐步扩展到其他顶点，找到最短路径。

 Dijkstra算法的基本原理
 Dijkstra算法的主要思想是维护一个距离数组，用于记录源顶点到每个顶点的当前最短距离。算法通过以下步骤进行：

 初始化：选择一个源顶点，并初始化距离数组，将源顶点到自身的距离设为0，其他顶点到源顶点的距离设为无穷大。
 选择未访问顶点：每次从未访问的顶点中选择距离最短的顶点。
 更新距离：对于选定的顶点，更新其相邻顶点的距离，如果通过选定顶点到达某个顶点的距离更短，则更新该顶点的距离。
 重复：重复上述步骤，直到所有顶点都被访问过。
 Dijkstra算法的步骤
 初始化：选择一个源顶点，并初始化距离数组和已访问、未访问的顶点集合。
 选择未访问顶点：从未访问的顶点中选择距离最小的顶点。
 更新距离：对于选定的顶点，更新其所有相邻顶点的距离，如果通过选定顶点到达某个顶点的距离更短，则更新该顶点的距离。
 标记为已访问：将选定的顶点标记为已访问。
 重复：重复步骤2-4，直到所有顶点都被访问过。
 Dijkstra算法的应用场景
 路径规划：例如，在一个城市网络中，每个顶点表示一个城市，每条边表示城市之间的道路距离，Dijkstra算法可以用来找到两个城市之间的最短路径。
 路由通信：在网络中，每个顶点表示一个路由器，每条边表示路由器之间的通信成本，Dijkstra算法可以用来找到最优的通信路径。
 工作任务规划：在工作中，每个顶点表示一个工作任务，每条边表示任务之间的依赖关系和时间成本，Dijkstra算法可以用来找到完成一系列任务的最短路径。
 通过上述解释，我们可以看到Dijkstra算法在解决单源最短路径问题中的广泛应用和重要性。‌
 */
public class SP_Dijkstra {

    public static void main(String[] args) {
        // 题目在下面
        int[][] edges =
                {{1,2,1},
                        {2,3,3},
                        {1,3,100}};
        int n = 3;
        int start = 1,end = 3;

        int result = minPath(n, edges, start, end);
        System.out.println(result);
    }

    public static int minPath(int n, int[][] edges, int start, int end) {
        // 预处理，使用矩阵构建图
        int[][] array = new int[n][n];
        for (int i = 0; i < edges.length; i++) {
            //节点当作坐标，元素为代价
            array[edges[i][0] -1 ][edges[i][1] -1] = edges[i][2];
            array[edges[i][1] -1 ][edges[i][0] -1] = edges[i][2];
        }
        final int cMax = Integer.MAX_VALUE;

        // 使用Dijkstra计算
        // 关键工具
        int[] dist = new int[n];// 表示编号是i+1的点到start点的最短惩罚值
        int[] path = new int[n];
        boolean[] collected = new boolean[n];// 记录某顶点是否被收录
        collected[start-1] = true;
        // 初始化
        for (int i = 0; i < n; i++) {
            if(i!=(start-1) && array[start-1][i] == 0){
                // 两个顶点不相连,惩罚设置为无穷大
                dist[i] = cMax;
            }else{
                dist[i] = array[start-1][i];
            }
        }
        for (int i = 0; i < n; i++) {
            if(i==(start-1)){
                path[i] = -1;
                continue;
            }
            if(array[start-1][i] == 0){
                path[i] = -1;
            }else {
                path[i] = start-1;
            }
        }
        // 核心算法
        while (true){
            // 未收录dist中最小值
            int v = minDist(dist,collected);

            if(v == -1){
                // 说明不存在
                break;
            }
            // 收录顶点v到最短路径中
            collected[v] = true;
            // 验证v顶点对邻接点的影响
            for (int w = 0; w < array[v].length; w++) {
                if(!collected[w]){// 未被收录的邻接点w
                    if( (dist[v] | array[v][w]) < dist[w]){
                        // 更新最短路
                        dist[w] = dist[v] | array[v][w];
                        path[w] = v;
                    }

                }
            }
        }
        // 获取最小惩罚值
        int result = dist[end-1];

        return result == cMax ? -1 : result;
    }

    /**
     * 返回dist数组中最小值的下标
     * @param dist
     * @return
     */
    private static int minDist(int[] dist,boolean[] collected) {
        int temp = Integer.MAX_VALUE;
        int index = -1;
        for (int i = 0; i < dist.length; i++) {
            // 从未收录的顶点中找到最小值
            if(!collected[i] && temp > dist[i]){
                temp = dist[i];
                index = i;
            }
        }
        return index;
    }
}
/**
 * 给定⼀个 ⽆向图 包含 N 个节点和 M 条边, 每条边 Mi 的代价为 Ci 。图中⼀条
 * 路径的惩罚是指对该路径上所有边的代价 Ci 执⾏位运算或（bitwise OR）操
 * 作得到的。假如⼀条路径上包含了边 M1，M2，M3 … … ，Mk，那么对应的惩
 * 罚就是 C1 OR C2 OR C3 OR ... OR Ck。（OR代表位运算或，即 “|” ）
 * <p>
 * 给定图上两个节点 start 和 end，找到从 start 到 end 的所有路径中惩罚
 * 值最⼩的路径，对应的最⼩惩罚值作为结果返回。如果路径不存在就返回 -1。
 * <p>
 * 注意：任意两个节点之间最多存在⼀条边，图中可能存在有环路
 * <p>
 *
 * 参数含义：
 * n：节点总数；节点编号从 1 开始，⼀直到 n，共有 n 个；
 * edges：⽆向图的边；edges[i] 表示边Mi，其中 edges[i][0] 和
 * edges[i][1] 是Mi的两个节点的编号，edges[i][2] 是Mi对应的代价 Ci；
 * start 和 end：路径的开始和结束节点编号
 *
 *  1 <= n <=1000
 *  1 <= edges.length <= 10000
 *  1 <= Ci <=1024
 *
 * edges = [ [1，2，1]，[2，3，3]，[1，3，100] ]
 *
 * 当 start = 1，end = 3 时，其最⼩惩罚路径是 1->2->3, C(1,2)=1并且C(2,3)=3,
 * 对应的惩罚值为 1 | 3 = 3。
 */